package cn.ljy.authority.service.impl;

import cn.ljy.authority.dao.mapper.SysPermissionMapper;
import cn.ljy.authority.dao.mapper.SysRoleMapper;
import cn.ljy.authority.dao.mapper.SysRolePermissionMapper;
import cn.ljy.authority.model.SysPermission;
import cn.ljy.authority.model.common.AuthorityConstant;
import cn.ljy.authority.model.query.SysPermisssionQuery;
import cn.ljy.authority.service.SysPermissionService;
import cn.ljy.authority.utils.MyObjectUtils;
import cn.ljy.common.exception.model.MyConfirmException;
import cn.ljy.common.exception.model.MyIgnorableException;
import cn.ljy.common.exception.model.MyServiceException;
import cn.ljy.common.exception.model.MyWebException;
import cn.ljy.common.utils.redis.StringRedisUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ISysPermissionService extends IBaseService<SysPermission, SysPermisssionQuery> implements SysPermissionService {

    @Autowired
    private SysPermissionMapper mapper;
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private SysRolePermissionMapper sysRolePermissionMapper;
    @Autowired
    private StringRedisUtils stringRedisUtils;


    /**
     * 初始化树形菜单
     * 说明：项目启动即将菜单存到redis缓存中，避免反复到数据库查询菜单，用于提升项目性能。
     * 注意事项：在项目菜单改变时，要及时更新redis中的菜单信息
     */
    @PostConstruct//项目启动时自动执行
    public void initTree(){
        //1.权限菜单
        List<SysPermission> permissionTree = mapper.findTree();
        stringRedisUtils.set(AuthorityConstant.PERMISSION_TREE, JSON.toJSONString(permissionTree));
        //2.树形菜单（不含按钮）
        List<SysPermission> menuTree = mapper.findTreeMenu();
        stringRedisUtils.set(AuthorityConstant.MENU_TREE, JSON.toJSONString(menuTree));
    }

    /**
     * 用于在菜单更新后，刷新redis中的菜单信息
     */
    @Override
    protected void afterChange() {
        this.initTree();
    }

    /**
     * 返回redis中存储的菜单信息
     * @param isMenuType
     * @return
     */
    private List<SysPermission> getPermissionTree(boolean isMenuType){
        List<SysPermission> permissionTree;
        if(isMenuType){//如果是菜单类型
            permissionTree=JSON.parseArray(stringRedisUtils.get(AuthorityConstant.MENU_TREE), SysPermission.class);;
        }else{
            permissionTree=JSON.parseArray(stringRedisUtils.get(AuthorityConstant.PERMISSION_TREE), SysPermission.class);;
        }
        return permissionTree;
    }


    /**
     * 设置关联属性
     * @param permission
     */
    private void setParam(SysPermission permission){
        //添加相关关联属性
        //使用角色数
        permission.setRoleNumber(sysRolePermissionMapper.countAllRoleByPermissionId(permission.getId()));
    }

    /**
     * 采用非递归形式，为树形集合设置关联属性
     * 说明：
     * 1.应该是树形结构，即每个点应该都带children
     * 2.如果要设置关联属性的不是树形结构（例如直接查询集合列表），则children不应该带值，否则会严重影响效率
     * @param tree
     */
    private void setTreePermissionsParam(List<SysPermission> tree){
        tree.stream().forEach(permission -> {
            //添加相关关联属性
            setParam(permission);
            List<SysPermission> childrens=permission.getChildren();
            List<SysPermission> childrensTmp=new ArrayList<>();//临时存储对象
            while(childrens!=null&&childrens.size()>0){
                for(SysPermission permissionChildren:childrens){
                    //添加相关关联属性
                    setParam(permissionChildren);
                    childrensTmp.addAll(permissionChildren.getChildren());
                }
                //遍历结束后
                childrens=new ArrayList<>();//清空集合，不用用clear，否则会把原集合的引用对象移除
                childrens.addAll(childrensTmp);//重置children集合
                childrensTmp=new ArrayList<>();//清空临时集合，不能用clear，需要重新更换地址
            }
        });
    }

    @Deprecated
    @Override
    public List<SysPermission> getTreeMenus(@Valid @NotEmpty List<SysPermission> permissions) {
        //将权限中的菜单筛选出来
        List<SysPermission> menus=permissions.stream()
                .filter(menu->menu.getType().equals(1)) //只要菜单
                .collect(Collectors.toList());
        //首先筛选出根目录菜单
        List<SysPermission> menusOfRoot=menus.stream().filter(menu->menu.getParentId().equals(Long.valueOf(0))).collect(Collectors.toList());
        //存储权限集合
        menus.removeAll(menusOfRoot);
        menusOfRoot.forEach(menuOfRoot->{
            setChildPermissons(menuOfRoot,menus);
        });
        return menusOfRoot;
    }

    @Override
    public List<SysPermission> findTree(SysPermisssionQuery sysPermisssionQuery) {
        List<SysPermission> list=new ArrayList<>();
        if(MyObjectUtils.checkObjAllFieldsIsNull(sysPermisssionQuery)) { // 如果查询条件为空
            list.addAll(mapper.findTree());
        }else{
            //1.首先从数据库查询出相关的权限集合（非树形）
            List<SysPermission> permissionListByQuery=mapper.findAll(sysPermisssionQuery);
            //2.创建一个map集合用于存储权限和其对应层级，并筛选掉不存在的权限id
            HashMap<SysPermission,Integer> permissionMap=new HashMap<>();//按节点对象->层级数
            for(SysPermission permission:permissionListByQuery){
                //获取去权限所在层级
                permissionMap.put(permission,this.getDeepOfPermission(0,permission.getId()));
            }
            //3.创建一个list集合来对上一步的map集合进行排序(按照权限层级，也就是value值进行升序排列)
            //即保证权限是先从上一层的顺序开始，避免出现传入的id1是在底层目录，id2是上一级目录。这样就会出现虽然id1、id2都是一个树形目录下，但是却要分别查找两次，同时增大按照树形目录的删除要从底层开始的难度。
            //备注说明：不要使用treemap类型来进行排序，因为treemap不允许元素重复（重写根据值来进行判断的方法后）就会出现值相等的话，元素判定为重复，即相互覆盖
            List<Map.Entry<SysPermission, Integer>> permissionMapListOfSort = new ArrayList<>(permissionMap.entrySet());
            Collections.sort(permissionMapListOfSort, new Comparator<Map.Entry<SysPermission, Integer>>() {
                public int compare(Map.Entry<SysPermission, Integer> o1, Map.Entry<SysPermission, Integer> o2) {
                    return o1.getValue().compareTo(o2.getValue()); // 按照值升序排列
                }
            });
            //4.定义一个权限列表，用于存储所有关联的权限（包括子权限-递归），从而为后续删除某个权限的树形节点做准备。
            //保存格式为：栏目节点1-节点1子栏目1-节点1子子栏目11-节点1子子栏目12-节点1子栏目2--节点1子子栏目1--栏目节点2-节点2-子栏目1-……）的顺序
            List<SysPermission> permissionList=new ArrayList();//该集合定义仅用于筛查重复点
            for(Map.Entry<SysPermission,Integer> permissionMapOfEach:permissionMapListOfSort){
                //首先判断这个节点是不是已经在之前转换的树形集合中（提高查找性能）
                SysPermission permission=permissionMapOfEach.getKey();
                if(permissionList.contains(permission)){
                    //如果这个节点已经在之前的节点树形目录中，则不在进行查找
                    continue;
                }
                permission=mapper.getTreeById(permission.getId());//获取树形节点
                //添加属性集合到节点
                list.add(permission);
                //将这个树形菜单节点及所有子节点，按照父目录-子目录的形式转换成List集合
                this.transferPermissionListFromPermissionTree(permission,permissionList);
            }
        }
        //遍历树形结构：使用非递归方式
//        setTreePermissionsParam(tree);
        return list;
    }

    /**
     * 20240728废弃改方法
     * 20220221优化递归函数，解决了部分情况下出现递归错误的情况
     * @param menuOfParent
     * @param menus
     */
    @Deprecated
    private void setChildPermissons(SysPermission menuOfParent,List<SysPermission> menus){
        List<SysPermission> menusOfNext=new ArrayList<>();//定义一个记录menuOfParent所包含的子节点的菜单集合，用于遍历，避免iterator重复迭代出错
        Iterator it=menus.iterator();
        while(it.hasNext()){//遍历剩余菜单
            SysPermission menu=(SysPermission)it.next();//获取当前菜单
            if(menu.getParentId().equals(menuOfParent.getId())){//如果该菜单是当前菜单的子菜单
                menuOfParent.getChildren().add(menu);//添加该菜单进集合
                it.remove();//移除该菜单，后面不再进行判断
                //注意，这里不能再继续递归，否则可能在某些情况出现ConcurrentModificationException错误。
                //例如在二级菜单中的第一个菜单有子菜单，那么递归遍历第二个菜单的时候就会出错
                //this.setChildPermissons(menu,menus);
                menusOfNext.add(menu);//用于进入下一次迭代
            }
        }
        //进行递归查询
        menusOfNext.forEach(menuOfNext->{
            this.setChildPermissons(menuOfNext,menus);
        });
    }


    @Override
    public List<SysPermission> getTreePermission(Long userId,boolean isMenu) {
        List<SysPermission> permissionsTree=new ArrayList<>();
        //如果是超级用户
        if(super.isSyperUser(userId.longValue())){
            if(isMenu){//如果要查找菜单类型的权限
                //直接获取树形菜单（不含按钮）
//                permissionsTree=mapper.findTreeMenu();
                permissionsTree=this.getPermissionTree(true);
            }else{
                permissionsTree=this.getPermissionTree(false);
            }
        }else{//如果是普通用户
            //不管是查找菜单还是权限，都必须是通过完整的树形权限菜单来进行判断
            permissionsTree.addAll(this.getPermissionTree(false));//获取数据库所有权限的树形权限
            //获取账户拥有的所有权限，注意查找结果必须是所有权限（含按钮），否则无法追溯到数据库中未保存关系的父级菜单
            Long[] permissionIds=mapper.findIdsByUserIdAndRoleEnabled(userId,true);//获得管理员拥有的菜单id集合
            List<Long> permissionIdList = Arrays.asList(permissionIds);//注意这样转换的集合不是真正的集合，无法使用add和remove方法
            permissionIdList=new CopyOnWriteArrayList<Long>(permissionIdList);//数组直接转换的集合如果用add和remove会出现异常
            try {
                this.setUserTreePermission(permissionsTree,permissionIdList,isMenu);//将用户拥有的菜单在树形菜单上进行标记
            } catch (MyIgnorableException e) {
                //不进行任何处理，仅仅只是为了跳出递归
            }
        }
        return permissionsTree;
    }

    /**
     * 1.方法说明（递归）：根据用户拥有的菜单，对该网站的树形菜单进行处理，最后得到用户拥有的树形菜单完整结构
     * 2.方法设计：
     * （1）如果子菜单是用户拥有的菜单，则其父路径上的所有节点都属于用户的菜单（不是拥有父路径权限，只是把父路径都完整映射给控制层）
     * （2）如果某菜单是用户拥有的菜单，不代表其拥有的子菜单都是用户的菜单（即拥有菜单，不代表拥有子菜单的权限，具体由子菜单和用户关系决定）
     * 3.实现/设计思路：
     *（1）首先递归到最底层的菜单进行判断
     * 遍历某最底层的菜单集合
     * a.如果不是用户拥有的菜单，则将其移除，直到该层的非用户菜单都移除为止
     * b.如果是用户拥有的菜单，则保留，并且将状态改为true，表示该层有菜单是用户的菜单，再反向递归上去时判断父节点是否保留。
     * （2）然后逐层往父菜单走
     * a.如果某节点的底层拥有子菜单，则该节点予以保留；
     * b.如果某节点是用户的直接子菜单，则该节点予以保留
     * c.如果某节点不满足上述a和b的条件，则删除该节点
     * @param permissions 系统中完整的树形菜单（包含任何类型的菜单），本方法会对该属性进行处理，保证最后该集合是用户拥有的树形菜单完整结构。注意这个树形菜单必须是完整的
     * @param permissionIds 用户拥有的菜单id
     * @param isMenu 查找的是否是菜单类型的权限，true表示是；false表示查找所有类型的权限
     * @return permissions集合中是否包含有用户拥有的权限（含按钮）
     */
    private boolean setUserTreePermission(List<SysPermission> permissions,List<Long> permissionIds,boolean isMenu){
        boolean result=false;//表示该菜单是否是用户菜单，在递归中返会给父目录让其知道子菜单中是否有用户菜单（true）
        if(permissionIds.size()>0){//如果用户还有菜单没有归属（找到一个菜单会删除一个）
            Iterator it=permissions.iterator();
            while(it.hasNext()){//遍历循环删除必须用迭代器来完成
                SysPermission permission=(SysPermission)it.next();
                List<SysPermission> permissionChildren=permission.getChildren();
                if(permissionChildren.size()>0){//只要有子节点就一直往下走，直到最底级节点
                    if(setUserTreePermission(permissionChildren,permissionIds,isMenu)) {//如果该菜单的子权限是用户拥有的。此处一直递归，直到最低级，然后再逐层返上来，让父菜单知道子菜单是否有用户菜单
                        if(isMenu){ // 如果要查找的是菜单类型
                            if(permission.getType()==2){//如果包含的菜单是按钮类型，则从树形菜单中移除该按钮
                                it.remove();//移除该菜单
                            }
                        }
                        result = true;//这里返回true，从而实现父节点都知道子菜单有用户拥有的菜单
                    }else{//如果在该菜单的子菜单中（递归）没有用户菜单
                        if(permissionIds.contains(permission.getId())) {//如果该菜单是直接拥有的菜单
                            permissionIds.remove(permission.getId());//移除该Id对象，下次不在进行判断
                            if(isMenu){ // 如果要查找的是菜单类型
                                if(permission.getType()==2){//如果包含的菜单是按钮类型，则从树形菜单中移除该按钮
                                    it.remove();//移除该菜单
                                }
                            }
                            result=true;
                        } else{
                            it.remove();//移除该菜单
                        }
                    }
                }else{//如果改菜单没有子菜单（即只有到了最底层目录，才进行判断）
                    if(permissionIds.contains(permission.getId())) {//如果该菜单是直接拥有的菜单
//                        System.out.println(permission.getName());
                        permissionIds.remove(permission.getId());//移除该Id对象，提升性能
                        if(isMenu){ // 如果要查找的是菜单类型
                            if(permission.getType()==2){//如果包含的菜单是按钮类型，则从树形菜单中移除该按钮
                                it.remove();//移除该菜单
                            }
                        }
                        result=true;//不管包含的权限是菜单还是按钮，都返回true，完成父级菜单的追溯
                    }else{//如果该菜单不属于用户直接拥有的菜单
                        it.remove();//移除该菜单
                    }
                }
            }
        }
        return result;
    }


    @Override
    protected void beforeGet(Serializable id) {

    }



    /**
     * 根据业务需求重置权限属性，防止前端传错，主要用于添加和编辑
     * @param sysPermission
     */
    private void validateAndResetParam(SysPermission sysPermission){
        if(sysPermission.getParentId()==null){ // 如果为null则说明是根目录菜单
            sysPermission.setParentId(0l);
        }
        if(sysPermission.getType()==1){ //如果是菜单类型
            if(sysPermission.getLinkType()==null){
                throw new MyWebException("操作失败：打开方式不能为空");
            }
            switch (sysPermission.getLinkType()){
                case 1:// 如果打开方式为组件
                    if(!StringUtils.hasLength(sysPermission.getPath())){
                        throw new MyWebException("验证失败：组件地址不能为空");
                    }
                    sysPermission.setUrl(null);
                    break;
                case 2://如果打开方式为内链
                    sysPermission.setPath(null);
                    sysPermission.setPermission(null);
                    break;
                case 3://如果打开方式为外链
                    if(StringUtils.hasLength(sysPermission.getUrl())){ //如果地址存在
                        Pattern urlRattern= Pattern.compile("^(http|https)://");
                        if(!urlRattern.matcher(sysPermission.getUrl()).find()){//如果不匹配http或https开头
                            throw new MyWebException("操作失败：外部链接必须以http://或https://开头");
                        }
                    }else{
                        throw new MyWebException("操作失败：外部链接不能为空");
                    }
                    sysPermission.setPath(null);
                    sysPermission.setPermission(null);
            }
        }else{ //如果菜单类型为按钮
            sysPermission.setLinkType(null);
            sysPermission.setComponent(null);
            sysPermission.setRoute(null);
            sysPermission.setPath(null);
            sysPermission.setUrl(null);
        }
    }



    @Override
    protected void beforeSave(SysPermission sysPermission) {
        validateAndResetParam(sysPermission);
        //判断组件名是否重名
        if(sysPermission.getType()==1) { //如果是菜单类型
            if(mapper.countByComponent(sysPermission.getComponent())>0){
                throw new MyServiceException("编辑失败：组件名已存在");
            }
        }
    }

    @Override
    protected void beforeUpdate(SysPermission sysPermission) {
        if(sysPermission.getId()==null){
            throw new MyWebException("编辑失败：标识符不能为空");
        }
        if(sysPermission.getId().equals(sysPermission.getParentId())){
            throw new MyServiceException("编辑失败：不能选择自己作为上级菜单");
        }
        validateAndResetParam(sysPermission);
        //判断组件名是否重名
        if(sysPermission.getType()==1) { //如果是菜单类型
            if(mapper.countByComponentAndIdNot(sysPermission.getComponent(),sysPermission.getId())>0){
                throw new MyServiceException("编辑失败：组件名已存在");
            }
        }
    }

    @Override
    protected void beforeDelete(Serializable[] ids) {
        //获取角色和权限关系的错误信息集合
        Map<String,Integer> errorMessageMapOfRolePermission=new HashMap();
        int rolePermissionNumberSum=0;//用于记录要删除的关联权限对应的所有角色数量
        //获取所有要删除的关联权限集合，例如要删除节点的子节点
        Serializable[] permissionIdsOfAllAssociated=this.getPermissionIdsOfAllAssociated(ids);
        //遍历所有关联的权限节点
        for(Serializable id:permissionIdsOfAllAssociated){
            if(id!=null){
                //获取指定权限信息
                SysPermission permission = mapper.findById(id);
                if(permission!=null){
                    //1.先判断是否有菜单。后允许移除子菜单，故注释相关验证
//                    SysPermisssionQuery sysPermisssionQuery=new SysPermisssionQuery();
//                    sysPermisssionQuery.setParentId(Long.valueOf(id.toString()));
//                    if(mapper.count(sysPermisssionQuery)>0){//如果还有子菜单
//                        throw new MyServiceException("删除失败：请先删除权限（"+permission.getName()+"）的子权限信息");
//                    }
                    //2.获取指定权限和多少个角色有关联
                    int rolePermissionNumber=sysRolePermissionMapper.countAllRoleByPermissionId(id);
                    rolePermissionNumberSum+=rolePermissionNumber;
                    //判断是否有角色映射到该菜单
                    if(rolePermissionNumber>0){
                        errorMessageMapOfRolePermission.put(permission.getName(),rolePermissionNumber);
                    }
                }
            }
        }
        //定义错误信息
        StringBuilder errorMessage=new StringBuilder();
        if(errorMessageMapOfRolePermission.size()>0){
            int index=0;//索引
            errorMessage.append("权限删除失败：");
            //循环处理错误信息
            for(String permissionName:errorMessageMapOfRolePermission.keySet()){
                errorMessage.append("权限"+permissionName+"有"+errorMessageMapOfRolePermission.get(permissionName)+"个角色使用");
                if(index==errorMessageMapOfRolePermission.size()-1){
                    errorMessage.append("。共计"+errorMessageMapOfRolePermission.size()+"个关联权限，"+rolePermissionNumberSum+"个关联角色");
                    //最后一句话结尾不进行处理，是为了交给前端来决定最后一句话的符号结尾。
//                    errorMessage.append("。");
                }else{
                    errorMessage.append("，");
                }
                index++;
            }
            throw new MyConfirmException(errorMessage.toString());
        }
    }

    //因为要控制关联权限的删除，这里直接重写forceDelete函数
    @Override
    public void forceDelete(@NotEmpty(message = "操作失败：请选择要移除的记录") Serializable[] ids) {
        //获取所有要删除的关联权限集合，例如要删除节点的子节点
        Serializable[] permissionIdsOfAllAssociated=this.getPermissionIdsOfAllAssociated(ids);
        //删除权限对应的角色关系表
        sysRolePermissionMapper.deleteByPermissionIds(permissionIdsOfAllAssociated);
        //删除权限集合
        mapper.deleteByIdIn(permissionIdsOfAllAssociated);
    }

    /**
     * 20240828最后由Ljy更新
     * 根据传入的权限id集合，进行一系列处理后返回带有所有关联的权限id集合（例如要删除节点的子节点）
     * 说明：
     * 1.能够根据权限id集合获取其子节点（递归）的所有节点集合并返回
     * 2.如果权限id在数据库不存在，则进行移除。
     * 3.重要：返回的权限id集合是这个结构的逆转形式（栏目节点1-节点1子栏目1-节点1子子栏目11-节点1子子栏目12-节点1子栏目2--节点1子子栏目1--栏目节点2-节点2子栏目1-……）的顺序）
     * 即（……-节点2子栏目1-栏目节点2-节点1子子栏目1-节点1子栏目2-节点1子子栏目12-节点1子子栏目11-节点1子栏目1-栏目节点1）也就是说可以直接按照顺序从数据库移除。
     * @param permissionIds
     * @return
     */
    private Serializable[] getPermissionIdsOfAllAssociated(Serializable[] permissionIds){
        //1.创建一个map集合用于存储权限和其对应层级，并筛选掉不存在的权限id
        HashMap<SysPermission,Integer> permissionMap=new HashMap<>();//按节点对象->层级数
        for(Serializable id:permissionIds) {
            if (id == null) {
                continue;
            }
            SysPermission sysPermission=mapper.findByIdNoRelation(id);//为了性能，这里没有使用getTreeById方式。
            if(sysPermission!=null){
                permissionMap.put(sysPermission,this.getDeepOfPermission(0,sysPermission.getId()));
            }
        }
        //2.创建一个list集合来对上一步的map集合进行排序(按照权限层级，也就是value值进行升序排列)
        //即保证权限是先从上一层的顺序开始，避免出现传入的id1是在底层目录，id2是上一级目录。这样就会出现虽然id1、id2都是一个树形目录下，但是却要分别查找两次，同时增大按照树形目录的删除要从底层开始的难度。
        //备注说明：不要使用treemap类型来进行排序，因为treemap不允许元素重复（重写根据值来进行判断的方法后）就会出现值相等的话，元素判定为重复，即相互覆盖
        List<Map.Entry<SysPermission, Integer>> permissionMapListOfSort = new ArrayList<>(permissionMap.entrySet());
        Collections.sort(permissionMapListOfSort, new Comparator<Map.Entry<SysPermission, Integer>>() {
            public int compare(Map.Entry<SysPermission, Integer> o1, Map.Entry<SysPermission, Integer> o2) {
                return o1.getValue().compareTo(o2.getValue()); // 按照值升序排列
            }
        });
        //3.定义一个权限列表，用于存储所有关联的权限（包括子权限-递归），从而为后续删除某个权限的树形节点做准备。
        //保存格式为：栏目节点1-节点1子栏目1-节点1子子栏目11-节点1子子栏目12-节点1子栏目2--节点1子子栏目1--栏目节点2-节点2-子栏目1-……）的顺序
        //这个格式进行逆转后，可以保证树形目录从最底层开始移除。
        List<SysPermission> permissionList=new ArrayList();
        for(Map.Entry<SysPermission,Integer> permissionMapOfEach:permissionMapListOfSort){
//            System.out.print("权限id:"+permissionMapOfEach.getKey().getId());
//            System.out.println("权限所在层级"+permissionMapOfEach.getValue());
            //首先判断这个节点是不是已经在之前转换的树形集合中（提高查找性能）
            SysPermission permission=permissionMapOfEach.getKey();
            if(permissionList.contains(permission)){
                //如果这个节点已经在之前的节点树形目录中，则不在进行查找
                continue;
            }
            permission=mapper.getTreeById(permission.getId());//获取树形节点
            //将这个树形菜单节点及所有子节点，按照父目录-子目录的形式转换成List集合
            this.transferPermissionListFromPermissionTree(permission,permissionList);
        }
        //4.重要：将该列表逆转，实现从底层删除
        Collections.reverse(permissionList);//将集合反转，使得后面的处理可以从最低层目录开始
        //5.将权限id提取出来供业务层处理
        Serializable[] permissionIdsOfAllAssociated=permissionList
                .stream()
                .map(SysPermission::getId)
                .toArray(Serializable[]::new);
        return permissionIdsOfAllAssociated;
    }

    /**
     * 获取指定节点在数据库的层级
     * 特别说明：使用该函数时要考虑性能，大量节点的父类节点不适合使用该方法判断
     * @param deep 调用时先传入0
     * @param id 指定节点id
     * @return 1表示第一层，2表示第二层，以此类推
     */

    private int getDeepOfPermission(int deep,Serializable id){
        //首先用于在递归时进行初始赋值
        if(deep==0){
            deep=1;
        }
        //获取父节点
        long parentId=mapper.getParentId(id);
        if(parentId!=0){//如果该节点不是根节点
            deep++;
            return getDeepOfPermission(deep,parentId);
        }
        return deep;
    }

    /**
     * 用于递归读取菜单及其子菜单
     * 说明：
     * 1.如果是用于读取，可以直接使用该集合
     * 2.如果是用于删除，按照从最低级目录删除的形式，则该集合可以从最后一个逆向上来删除
     * @param permission 当前菜单，要求是tree类型
     * @param list 应当传入创建好的空集合，最终该集合按照该菜单及其所有子菜单的目录顺序（如当前栏目-子栏目1-子子栏目11-子子栏目12-子栏目2--子子栏目1）的顺序
     */
    private void transferPermissionListFromPermissionTree(SysPermission permission,List<SysPermission> list){
        if(!list.contains(permission)){//只有该节点不在原有的列表中才进行添加
            list.add(permission);
            List<SysPermission> childrens=permission.getChildren();//获取子目录
            if(childrens.size()>0){
                for(SysPermission permissionChildren:permission.getChildren()){  //遍历子目录
                    transferPermissionListFromPermissionTree(permissionChildren,list);
                }
            }
        }
    }
}
